﻿// Jinx AJAX Framework
// Copyright (C) 2008 Blake Hegerle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Formality.Game;

namespace Formality.Levels
{
    public class LevelEnumerator<T>
        where T : class
    {
        private readonly string Name;
        private readonly Func<string, T> Parser;
        private IEnumerable<Sentence<T>> SentenceList;

        public LevelEnumerator(string Name, Func<string, T> Parser)
        {
            this.Name = Name;
            this.Parser = Parser;
        }

        public IEnumerable<Sentence<T>> Sentences
        {
            get
            {
                if (SentenceList == null)
                    SentenceList = LevelParser.ToList();

                return SentenceList;
            }
        }

        private IEnumerable<Sentence<T>> LevelParser
        {
            get
            {
                var QName = typeof (LevelEnumerator<>).Namespace + '.' + Name;

                using (var Stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(QName))
                {
                    if (Stream == null)
                        throw new Exception("Could not open " + QName);

                    using (var Reader = new StreamReader(Stream))
                    {
                        var Doc = XDocument.Load(Reader);
                        var LevelContents = (from Level in Doc.Descendants("level")
                                             from Sent in Level.Descendants("s")
                                             select new
                                                        {
                                                            Level = (string) Level.Attribute("id"),
                                                            Id = (string) Sent.Attribute("id"),
                                                            Name = (string) Sent.Attribute("name"),
                                                            Parsed = TryParse(Sent.Value),
                                                            Input = Sent.Value
                                                        });

                        var FirstError = LevelContents.FirstOrDefault(p => p.Parsed == null);

                        if (FirstError != null)
                            throw new Exception(string.Format("Could not parse {0} {1}", FirstError.Id, FirstError.Input));

                        var SentenceQuery = from s in LevelContents
                                            select new
                                                       {
                                                           Id = "*" + s.Level + (s.Id == null ? "" : "." + s.Id) + ".",
                                                           DisplayId = s.Name,
                                                           s.Parsed
                                                       };

                        return from s in SentenceQuery
                               select new Sentence<T>(s.Id, s.DisplayId ?? s.Id, s.Parsed);
                    }
                }
            }
        }

        private T TryParse(string Input)
        {
            try
            {
                return Parser(Input);
            }
            catch
            {
                return null;
            }
        }
    }
}